package com.ifp.opengate.boot.factory;

import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.RequestRateLimiterGatewayFilterFactory;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
import org.springframework.cloud.gateway.route.Route;
import org.springframework.cloud.gateway.support.HttpStatusHolder;
import org.springframework.cloud.gateway.support.ServerWebExchangeUtils;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.stereotype.Component;

import com.ifp.adapter.common.AdapterCanstants;
import com.ifp.core.context.ClogicContext;
import com.ifp.core.log.Trace;
import com.ifp.opengate.boot.bean.OpenGateRoute;
import com.ifp.opengate.boot.constants.Constants;
import com.ifp.opengate.boot.emum.ErrorEnum;
import com.ifp.opengate.boot.task.ConnectorCacheDataTask;
import com.ifp.opengate.boot.utils.MailServiceUtils;
import com.ifp.opengate.boot.utils.RedisUtils;

import reactor.core.publisher.Mono;

/**
 * 项目名：ifp-gateway <br>
 * 包名：com.ifp.opengate.boot.factory <br>
 * 类名：IFPRequestRateLimiterGatewayFilterFactory <br>
 * 描述： 自定义限流处理
 * 
 * @author liulx
 * @create 2021-06-11 09:17
 */
@Component
public class IfpRequestRateLimiterGatewayFilterFactory extends RequestRateLimiterGatewayFilterFactory {

    @Autowired
    RedisUtils redisUtils;

    @Autowired
    private ConnectorCacheDataTask connectorCacheDataTask;

    @Autowired
    private MailServiceUtils mailServiceUtils;

    private final RateLimiter defaultRateLimiter;

    private final KeyResolver defaultKeyResolver;

    private boolean denyEmptyKey = true;

    public IfpRequestRateLimiterGatewayFilterFactory(RateLimiter defaultRateLimiter, KeyResolver defaultKeyResolver) {
        super(defaultRateLimiter, defaultKeyResolver);
        this.defaultKeyResolver = defaultKeyResolver;
        this.defaultRateLimiter = defaultRateLimiter;

    }

    @Override
    public GatewayFilter apply(RequestRateLimiterGatewayFilterFactory.Config config) {
        KeyResolver resolver = (KeyResolver)this.getOrDefault(config.getKeyResolver(), this.defaultKeyResolver);
        RateLimiter<Object> limiter = (RateLimiter)this.getOrDefault(config.getRateLimiter(), this.defaultRateLimiter);
        boolean denyEmpty = (Boolean)this.getOrDefault(config.getDenyEmptyKey(), this.denyEmptyKey);
        HttpStatusHolder emptyKeyStatus =
            HttpStatusHolder.parse((String)this.getOrDefault(config.getEmptyKeyStatus(), this.getEmptyKeyStatusCode()));
        return (exchange, chain) -> {
            long startMillis = System.currentTimeMillis();
            Route route = (Route)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
            return resolver.resolve(exchange).defaultIfEmpty("____EMPTY_KEY__").flatMap((key) -> {
                Trace.logInfo(Trace.MODULE_ACTION, "限流规则值：-->" + key);
                if ("____EMPTY_KEY__".equals(key)) {
                    if (denyEmpty) {
                        ServerWebExchangeUtils.setResponseStatus(exchange, emptyKeyStatus);
                        return exchange.getResponse().setComplete();
                    } else {
                        return chain.filter(exchange);
                    }
                } else {
                    // 获取routeid
                    String routeId = route.getId();
                    // 使用routeId+KeyResolver.hashcode获取config
                    // routeId = routeId + config.getKeyResolver().hashCode();
                    routeId = routeId + resolver.hashCode();
                    Trace.logInfo(Trace.MODULE_ACTION, "限流 routeId-->" + route);
                    return limiter.isAllowed(routeId, key).flatMap((response) -> {
                        Iterator var4 = response.getHeaders().entrySet().iterator();

                        while (var4.hasNext()) {
                            Map.Entry<String, String> header = (Map.Entry)var4.next();
                            try {
                                exchange.getResponse().getHeaders().add((String)header.getKey(),
                                    (String)header.getValue());
                            } catch (UnsupportedOperationException ignored) {

                            }
                        }

                        if (response.isAllowed()) {
                            Trace.logInfo(Trace.MODULE_ACTION, "限流放行-->{},{}",
                                exchange.getAttributes().get(Constants.GLOBALLOGID), route.getId());
                            return chain.filter(exchange).then(Mono.fromRunnable(() -> {
                                Trace.logInfo(Trace.MODULE_ACTION, "rateLimiter this request {} cost time is {}",
                                    exchange.getAttributes().get(Constants.GLOBALLOGID),
                                    System.currentTimeMillis() - startMillis);
                            }));
                        } else {
                            // ServerWebExchangeUtils.setResponseStatus(exchange, config.getStatusCode());
                            // return exchange.getResponse().setComplete();
                            Trace.logInfo(Trace.MODULE_ACTION, "访问已限流-->" + route);
                            /*return reactor.core.publisher.Mono.error(new AccessControlException(
                                    ErrorEnum.merchantCurrentCotrolError.code(), ErrorEnum.merchantCurrentCotrolError.msg()
                            ));*/

                            Map<String, Object> errorAttributes = new LinkedHashMap();
                            errorAttributes.put("timestamp", new Date());
                            errorAttributes.put("status", config.getStatusCode().value());

                            OpenGateRoute openGateRoute = connectorCacheDataTask.getRouteOfMap().get(route.getId());
                            if (openGateRoute.getRoutePath().startsWith(key)) {
                                ServerWebExchangeUtils.setResponseStatus(exchange, config.getStatusCode());
                                Trace.logInfo(Trace.MODULE_ACTION, "请求的接口 {} 访问已限流：-->" + key);
                                errorAttributes.put("message",
                                    String.format(ErrorEnum.currentLimitingError.msg(), openGateRoute.getRouteName()));
                            } else {
                                ClogicContext clogicContext =
                                    (ClogicContext)exchange.getAttributes().get(Constants.CL_CONTEXT);
                                Map<String, Object> reqDataMap = clogicContext.getDataMap();
                                Map<String, Object> headerMap =
                                    (Map<String, Object>)reqDataMap.get(AdapterCanstants.HEADER);
                                String currentChannel = (String)headerMap.get("channel");

                                // 限流告警邮件
                                mailServiceUtils.sendMailForRateLimit(route, key, currentChannel);

                                // String hostAddress =
                                // exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
                                Trace.logInfo(Trace.MODULE_ACTION, "请求的 ip {} 访问已限流", key);
                                errorAttributes.put("message",
                                    String.format(ErrorEnum.accessIpInBlackListError.msg(), key));
                                ServerWebExchangeUtils.setResponseStatus(exchange, config.getStatusCode());
                            }
                            errorAttributes.put("path", openGateRoute.getRoutePath());
                            errorAttributes.put("error", config.getStatusCode().getReasonPhrase());
                            DataBuffer buffer = exchange.getResponse().bufferFactory()
                                .wrap(JSONObject.wrap(errorAttributes).toString().getBytes(StandardCharsets.UTF_8));
                            exchange.getResponse().writeWith(Mono.just(buffer));
                            return exchange.getResponse().writeWith(Mono.just(buffer));

                            // 以下是自定义返回错误信息
                            // ServerHttpResponse httpResponse = exchange.getResponse();
                            // httpResponse.setStatusCode(config.getStatusCode());
                            //
                            // Map<String, Object> dataMap = new HashMap<>(4);
                            // dataMap.put("errorCode", ErrorEnum.merchantCurrentCotrolError.code());
                            // dataMap.put("errorMsg", ErrorEnum.merchantCurrentCotrolError.msg());
                            //
                            // // DataBuffer buffer =
                            // // httpResponse.bufferFactory().wrap(data.getBytes(StandardCharsets.UTF_8));
                            // DataBuffer buffer = httpResponse.bufferFactory()
                            // .wrap(JSONObject.wrap(dataMap).toString().getBytes(StandardCharsets.UTF_8));
                            // return httpResponse.writeWith(Mono.just(buffer));
                        }
                    });
                }
            });
        };
    }

    private <T> T getOrDefault(T configValue, T defaultValue) {
        return configValue != null ? configValue : defaultValue;
    }

}
